package gofunc

import (
	"encoding/json"
	"errors"
	"fmt"
	"github.com/parnurzeal/gorequest"
	"github.com/tatsushid/go-fastping"
	"log"
	"net"
	"os/exec"
	"regexp"
	"runtime"
	"strconv"
	"time"
)

type IpFunc struct {
}
type cmdout struct {
	Msg []byte
}

func (self *cmdout) Write(p []byte) (n int, err error) {
	self.Msg = append(self.Msg, p...)
	return len(p), nil
}

// 获取本地的ip地址
func (self *IpFunc) GetInternal() (error, string) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return err, ""
	}
	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				return nil, ipnet.IP.String()
			}
		}
	}
	return errors.New("不能找到本地ip"), ""
}

// 获取本机IP地址如果有多个，优先返回开头是192的
func GetInternal192() (error, string) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return err, ""
	}
	var ipaddrs []string
	for _, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				//return nil, ipnet.IP.String()
				ipaddrs = append(ipaddrs, ipnet.IP.String())
			}
		}
	}
	if len(ipaddrs) == 0 {
		return errors.New("不能找到本地ip"), ""
	}

	// 可能有内部透传用的ip，优先用192开头的
	if len(ipaddrs) == 1 {
		return nil, ipaddrs[0]
	}

	for _, ip := range ipaddrs {
		if ip[:3] == "192" {
			return nil, ip
		}
	}
	// 如果都不是，就返回第一个
	return nil, ipaddrs[0]

}

// 获取网卡地址通过网卡名 如eth0，eth1，ifconfig
func (self *IpFunc) GetLinuxEthIpByIfconfig(eth string) (ip string, err error) {
	if runtime.GOOS != "linux" {
		return "", errors.New("不是linux系统，当前系统为" + runtime.GOOS)
	}
	//cmdstr := "ifconfig | grep -A1 '^eth0' | grep 'inet addr:' | awk -F: '{ print $2 }' | awk '{ print $1 }'"
	cmdstr := "ifconfig | grep -A1 '^" + eth + "' | grep 'inet addr:' | awk -F: '{ print $2 }' | awk '{ print $1 }'"
	cmd := exec.Command("sh", "-c", cmdstr)
	out := &cmdout{}
	cmd.Stdout = out
	err = cmd.Run()
	if err != nil {
		log.Println(err.Error())
		return "", err
	}
	//fmt.Println(string(out.Msg))
	return string(out.Msg), nil
}

// 获取本地的Mac地址
func (self *IpFunc) GetMac() (error, string, net.HardwareAddr) {
	var index int
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return err, "", nil
	}
	for k, a := range addrs {
		if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
			if ipnet.IP.To4() != nil {
				index = k
				break
			}
		}
	}
	interfaces, err := net.Interfaces()
	if err != nil {
		return err, "", nil
	}
	//如果根据ip得到的网卡是空的，比如在扬创里面就是这样，那么我就遍历所有网卡，指定可以得到一个mac地址
	if interfaces[index].HardwareAddr.String() == "" {
		for _, v := range interfaces {
			if v.HardwareAddr.String() != "" {
				return nil, v.HardwareAddr.String(), v.HardwareAddr
			}
		}
	} else {
		return nil, interfaces[index].HardwareAddr.String(), interfaces[index].HardwareAddr
	}
	return errors.New("不能获取mac地址"), "", nil
}

// 获取公网ip需要api
func (self *IpFunc) GetExternal() (error, string) {
	_, bodyBytes, errs := gorequest.New().Timeout(2 * time.Second).
		Get("http://myexternalip.com/raw").
		EndBytes()
	if len(bodyBytes) == 0 { //超时的时候什么都不会返回
		return errors.New("超时"), ""
	}
	if errs != nil {
		return errs[0], ""
	}
	reg := regexp.MustCompile(`(\d+)\.(\d+)\.(\d+)\.(\d+)`)
	ip := reg.FindString(string(bodyBytes))
	return nil, ip
}

// 获取地理位置通过公网ip
func (self *IpFunc) GetPositionByIp(ip string) (error, string) {
	_, bodyBytes, errs := gorequest.New().Timeout(2*time.Second).
		Post("http://ip.taobao.com/service/getIpInfo.php").
		Param("ip", ip).
		EndBytes()
	if len(bodyBytes) == 0 { //超时的时候什么都不会返回
		return errors.New("超时"), ""
	}
	if errs != nil {
		return errs[0], ""
	}
	re := make(map[string]interface{})
	json.Unmarshal(bodyBytes, &re)
	data, ok := re["data"].(map[string]interface{})
	if !ok {
		return errors.New("返回结果不为json"), ""
	}
	//拼接地域 比如华南电信广东省云浮市
	s := fmt.Sprintf("%s%s%s%s", data["area"], data["isp"], data["region"], data["city"])
	return nil, s
}

func (self *IpFunc) GetLinuxDNS() (error, string) {
	if runtime.GOOS != "linux" {
		return errors.New("不是linux系统，当前系统为" + runtime.GOOS), ""
	}
	//直接在dns的配置文件获取只获取第一条
	cmdstr := "cat /etc/resolv.conf | sed -n '2p'|awk '{print $2}'|tr -d '\n'"
	//cmd := exec.Command("cat", "/etc/resolv.conf | awk '{print $2}'")
	cmd := exec.Command("sh", "-c", cmdstr)
	out := &cmdout{}
	cmd.Stdout = out
	err := cmd.Run()
	if err != nil {
		log.Println(err.Error())
		return err, ""
	}
	//fmt.Println(string(out.Msg))
	return nil, string(out.Msg)
}

func (self *IpFunc) GetLinuxGateway() (error, string) {
	if runtime.GOOS != "linux" {
		return errors.New("不是linux系统，当前系统为" + runtime.GOOS), ""
	}
	//直接在dns的配置文件获取只获取第一条
	cmdstr := "route -n | grep eth0 | grep UG | awk '{print $2}'|tr -d '\n'"
	//cmd := exec.Command("cat", "/etc/resolv.conf | awk '{print $2}'")
	cmd := exec.Command("sh", "-c", cmdstr)
	out := &cmdout{}
	cmd.Stdout = out
	err := cmd.Run()
	if err != nil {
		log.Println(err.Error())
		return err, ""
	}
	//fmt.Println(string(out.Msg))
	return nil, string(out.Msg)
}

func (self *IpFunc) GetLinuxMask() (error, string) {
	if runtime.GOOS != "linux" {
		return errors.New("不是linux系统，当前系统为" + runtime.GOOS), ""
	}
	//直接在dns的配置文件获取只获取第一条
	cmdstr := "ifconfig |grep inet| sed -n '1p'|awk '{print $4}'|awk -F ':' '{print $2}'|tr -d '\n'"
	//cmd := exec.Command("cat", "/etc/resolv.conf | awk '{print $2}'")
	cmd := exec.Command("sh", "-c", cmdstr)
	out := &cmdout{}
	cmd.Stdout = out
	err := cmd.Run()
	if err != nil {
		log.Println(err.Error())
		return err, ""
	}
	//fmt.Println(string(out.Msg))
	return nil, string(out.Msg)
}

func (self *IpFunc) SetLinuxIpAndMask(ip string, mask string) error {
	//ifconfig eth0 192.168.5.40 netmask 255.255.255.0
	if runtime.GOOS != "linux" {
		return errors.New("不是linux系统，当前系统为" + runtime.GOOS)
	}
	//直接在dns的配置文件获取只获取第一条
	cmdstr := "ifconfig eth0 " + ip + " netmask " + mask
	//cmd := exec.Command("cat", "/etc/resolv.conf | awk '{print $2}'")
	cmd := exec.Command("sh", "-c", cmdstr)
	out := &cmdout{}
	cmd.Stdout = out
	err := cmd.Run()
	if err != nil {
		log.Println(err.Error())
		return err
	}
	return nil
}

// ping 一次百度看是否有返回，这个函数中山网关不适合，因为他ping不通的时候不是堵塞而是返回一个err
func (self *IpFunc) Ping(addr string) error {
	log.SetFlags(log.Llongfile | log.LstdFlags)
	p := fastping.NewPinger()
	c := make(chan error, 2) //先放进去了2次
	ra, err := net.ResolveIPAddr("ip4:icmp", addr)
	if err != nil {
		return err
	}
	p.AddIPAddr(ra)
	p.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
		log.Println("IP Addr: %s receive, RTT: %v", addr.String(), rtt)
		c <- nil
	}
	//超时
	p.OnIdle = func() {
		c <- errors.New("ping失败超时" + addr)
	}
	err = p.Run()
	if err != nil {
		return err
	}
	err1 := <-c
	close(c)
	return err1
}

// 判断gprs网卡是否存在 ppp0
func (self *IpFunc) NetworkIsExit(eth string) bool {
	if runtime.GOOS != "linux" {
		log.Println("不是linux系统，当前系统为" + runtime.GOOS)
		return false
	}
	//直接在dns的配置文件获取只获取第一条
	cmdstr := "ifconfig | grep " + eth
	cmd := exec.Command("sh", "-c", cmdstr)
	out := &cmdout{}
	cmd.Stdout = out
	err := cmd.Run()
	if err != nil {
		log.Println(err.Error())
		return false
	}

	if string(out.Msg) == "" {
		return false
	} else {
		return true
	}
}

// golang判断端口是否被占用了
//
//	a := util.ScanPort("tcp","127.0.0.1",12001)
func (self *IpFunc) ScanPort(protocol string, hostname string, port int) bool {
	fmt.Printf("scanning port %d \n", port)
	p := strconv.Itoa(port)
	addr := net.JoinHostPort(hostname, p)
	conn, err := net.DialTimeout(protocol, addr, 2*time.Second)
	if err != nil {
		return false
	}
	defer conn.Close()
	return true
}
